--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit 500818941d24900ea4a7b3d48f95a639e68e320d
Parents : 8b5384e
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-05-09T20:33:57-05:00
feat(nomadnet): support query-parameter data in file downloads
Parse query strings from nomadnet URLs and forward them as request data
to link.request() for /file/ downloads, matching upstream NomadNet behavior.
Changes
4 files changed, 29 insertions(+), 17 deletions(-)
Diff
diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index 920db7b8..3c74e640 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -13967,6 +13967,7 @@ class ReticulumMeshChat:
destination_hash_hex = download_data.get("destination_hash")
file_path = download_data.get("file_path")
+ request_data = download_data.get("data")
if not destination_hash_hex or not file_path:
return
@@ -14109,9 +14110,10 @@ class ReticulumMeshChat:
downloader = NomadnetFileDownloader(
destination_hash,
file_path,
- on_file_download_success,
- on_file_download_failure,
- on_file_download_progress,
+ data=request_data,
+ on_file_download_success=on_file_download_success,
+ on_file_download_failure=on_file_download_failure,
+ on_file_download_progress=on_file_download_progress,
on_phase=on_file_download_phase,
reticulum=getattr(self, "reticulum", None),
)
diff --git a/meshchatx/src/backend/docs_manager.py b/meshchatx/src/backend/docs_manager.py
index 09afd669..d2f40796 100644
--- a/meshchatx/src/backend/docs_manager.py
+++ b/meshchatx/src/backend/docs_manager.py
@@ -341,7 +341,7 @@ class DocsManager:
return None
if not full_path.startswith(base + os.sep) and full_path != base:
return None
- if not os.path.exists(full_path):
+ if not os.path.isfile(full_path):
return None
with open(full_path, encoding="utf-8", errors="ignore") as f:
diff --git a/meshchatx/src/backend/nomadnet_downloader.py b/meshchatx/src/backend/nomadnet_downloader.py
index 732933b5..bd5e5ff4 100644
--- a/meshchatx/src/backend/nomadnet_downloader.py
+++ b/meshchatx/src/backend/nomadnet_downloader.py
@@ -282,6 +282,7 @@ class NomadnetFileDownloader(NomadnetDownloader):
on_file_download_success: Callable[[str, bytes], None],
on_file_download_failure: Callable[[str], None],
on_progress_update: Callable[[float], None],
+ data: str | None = None,
timeout: int | None = None,
*,
on_phase: Callable[[str], None] | None = None,
@@ -292,7 +293,7 @@ class NomadnetFileDownloader(NomadnetDownloader):
super().__init__(
destination_hash,
page_path,
- None,
+ data,
self.on_download_success,
self.on_download_failure,
on_progress_update,
diff --git a/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue b/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
index 516107b2..84cdf63e 100644
--- a/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
+++ b/meshchatx/src/frontend/components/nomadnetwork/NomadNetworkPage.vue
@@ -1937,9 +1937,11 @@ export default {
path = this.defaultNodePagePath;
}
+ const queryIndex = path.indexOf("?");
return {
destination_hash: null, // node hash was not in provided url
- path: path,
+ path: queryIndex >= 0 ? path.substring(0, queryIndex) : path,
+ query: queryIndex >= 0 ? path.substring(queryIndex + 1) : null,
};
}
@@ -1950,9 +1952,12 @@ export default {
// ensure destination is expected length
if (destinationHash.length === 32) {
+ const joined = relativeUrl.join(":");
+ const queryIndex = joined.indexOf("?");
return {
destination_hash: destinationHash,
- path: relativeUrl.join(":"),
+ path: queryIndex >= 0 ? joined.substring(0, queryIndex) : joined,
+ query: queryIndex >= 0 ? joined.substring(queryIndex + 1) : null,
};
}
}
@@ -1962,6 +1967,7 @@ export default {
return {
destination_hash: url,
path: this.defaultNodePagePath,
+ query: null,
};
}
@@ -2074,6 +2080,7 @@ export default {
this.downloadNomadNetFile(
destinationHash,
parsedUrl.path,
+ parsedUrl.query,
(fileName, fileBytesBase64) => {
// Calculate final download speed based on actual file size
if (this.nodeFileDownloadStartTime) {
@@ -2351,7 +2358,7 @@ export default {
const match = hash.match(/popout=([^&]+)/);
return match ? decodeURIComponent(match[1]) : null;
},
- downloadNomadNetFile(destinationHash, filePath, onSuccessCallback, onFailureCallback, onProgressCallback) {
+ downloadNomadNetFile(destinationHash, filePath, data, onSuccessCallback, onFailureCallback, onProgressCallback) {
try {
// set callbacks for nomadnet filePath download
this.nomadnetFileDownloadCallbacks[this.getNomadnetFileDownloadCallbackKey(destinationHash, filePath)] =
@@ -2362,15 +2369,17 @@ export default {
};
// ask reticulum to download file from nomadnet
- WebSocketConnection.send(
- JSON.stringify({
- type: "nomadnet.file.download",
- nomadnet_file_download: {
- destination_hash: destinationHash,
- file_path: filePath,
- },
- })
- );
+ const payload = {
+ type: "nomadnet.file.download",
+ nomadnet_file_download: {
+ destination_hash: destinationHash,
+ file_path: filePath,
+ },
+ };
+ if (data != null) {
+ payload.nomadnet_file_download.data = data;
+ }
+ WebSocketConnection.send(JSON.stringify(payload));
} catch (e) {
console.error(e);
}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────